< !DOCTYPE
html >
< html
lang = "en" >
< head >
< meta
charset = "UTF-8" >
< meta
name = "viewport"
content = "width=device-width, initial-scale=1.0" >
< title > BrewBean
Advanced
Sales
Forecast < / title >
< script
src = "https://cdn.tailwindcss.com" > < / script >
< script
src = "https://cdn.jsdelivr.net/npm/chart.js" > < / script >
< style >
body
{font - family: 'Arial', sans - serif;
background - color:  # f5f5f5; }
.card
{background: white;
border - radius: 12
px;
box - shadow: 0
4
px
6
px
rgba(0, 0, 0, 0.1);
padding: 20
px;
margin - bottom: 20
px;}
.input - group
{margin - bottom: 15px;}
label
{display: block;
margin - bottom: 5
px;
font - weight: 600;
color:  # 333; }
input, button
{width: 100 %;
padding: 10
px;
border: 1
px
solid  # ddd; border-radius: 6px; font-size: 16px; }
button
{background - color:  # 2563eb; color: white; border: none; cursor: pointer; transition: background 0.3s; }
     button: hover
{background - color:  # 1d4ed8; }
.adjustment - factors
{display: grid;
grid - template - columns: repeat(3, 1
fr); gap: 15
px;}
.result - card
{margin - top: 20px;}
table
{width: 100 %;
border - collapse: collapse;
margin: 15
px
0;}
th, td
{padding: 10px;
text - align: left;
border - bottom: 1
px
solid  # ddd; }
th
{background - color:  # f8fafc; }
.chart - container
{height: 300px;
margin: 20
px
0;}
< / style >
    < / head >
        < body


class ="container mx-auto px-4 py-8 max-w-6xl" >

< h1


class ="text-3xl font-bold text-center text-gray-800 mb-8" > BrewBean Advanced Sales Forecast < / h1 >

< !-- 输入区域 -->
< div


class ="card" >

< h2


class ="text-2xl font-semibold mb-4 text-gray-700" > Input Data < / h2 >

< div


class ="input-group" >

< label
for ="historical-data" > Historical Sales Data (units, comma-separated) < / label >
< input
type = "text"
id = "historical-data"
placeholder = "e.g., 120,135,150,160,140,155,170"
value = "120,135,150,160,140,155,170" >
< / div >

< h3


class ="text-xl font-semibold mt-6 mb-3 text-gray-700" > Adjustment Factors ( % ) < / h3 >

< div


class ="adjustment-factors" >

< div


class ="input-group" >

< label
for ="holiday" > Holiday / Event < / label >
< input
type = "number"
id = "holiday"
value = "0"
min = "-100"
max = "200" >
< / div >
< div


class ="input-group" >

< label
for ="weather" > Bad Weather < / label >
< input
type = "number"
id = "weather"
value = "0"
min = "-100"
max = "0" >
< / div >
< div


class ="input-group" >

< label
for ="promotion" > Promotion < / label >
< input
type = "number"
id = "promotion"
value = "0"
min = "0"
max = "200" >
< / div >
< / div >

< button
id = "calculate-btn"


class ="mt-6" > Calculate Forecast < / button >

< / div >

< !-- 结果区域 -->
< div


class ="card result-card" id="result-container" style="display: none;" >

< h2


class ="text-2xl font-semibold mb-4 text-gray-700" > Forecast Results < / h2 >

< div


class ="grid grid-cols-2 gap-6 mb-6" >

< div


class ="bg-blue-50 p-4 rounded-lg" >

< h3


class ="font-semibold text-gray-700" > 3-Day Moving Average Forecast (Next Day) < / h3 >

< p
id = "ma-forecast"


class ="text-2xl font-bold text-blue-600" > - < / p >

< / div >
< div


class ="bg-green-50 p-4 rounded-lg" >

< h3


class ="font-semibold text-gray-700" > Exponential Smoothing Forecast (Next Day) < / h3 >

< p
id = "es-forecast"


class ="text-2xl font-bold text-green-600" > - < / p >

< / div >
< / div >

< div


class ="chart-container" >

< canvas
id = "forecast-chart" > < / canvas >
< / div >

< h3


class ="text-xl font-semibold mt-6 mb-3 text-gray-700" > Detailed Calculations < / h3 >

< div


class ="overflow-x-auto" >

< table
id = "calculation-table" >
< thead >
< tr >
< th > Day < / th >
< th > Actual
Sales < / th >
< th > 3 - Day
Moving
Average < / th >
< th > Exponential
Smoothing < / th >
< / tr >
< / thead >
< tbody > < / tbody >
< / table >
< / div >
< / div >

< script >
// 计算逻辑
document.getElementById('calculate-btn').addEventListener('click', function()
{
// 获取输入数据
const
salesInput = document.getElementById('historical-data').value;
const
holiday = parseFloat(document.getElementById('holiday').value) / 100;
const
weather = parseFloat(document.getElementById('weather').value) / 100;
const
promotion = parseFloat(document.getElementById('promotion').value) / 100;

// 解析历史销售数据
const
actualSales = salesInput.split(',').map(Number).filter(num= > !isNaN(num));
if (actualSales.length < 3) {
alert('Please enter at least 3 days of historical sales data');
return;
}

// 计算调整因子总和
const
totalAdjustment = 1 + holiday + weather + promotion;

// 1.
计算3日移动平均
const
movingAverages = [];
for (let i = 2; i < actualSales.length; i++)
{
const
avg = (actualSales[i - 2] + actualSales[i - 1] + actualSales[i]) / 3;
movingAverages.push(avg);
}
const
maForecast = movingAverages[movingAverages.length - 1] * totalAdjustment;

// 2.
计算指数平滑（α = 0.3）
const
exponentialSmoothing = [120]; // 初始预测值Day1 = 120
for (let i = 1; i < actualSales.length; i++) {
    const
    es = 0.3 * actualSales[i - 1] + 0.7 * exponentialSmoothing[i - 1];
    exponentialSmoothing.push(es);
}
const
esForecast = 0.3 * actualSales[actualSales.length - 1] + 0.7 * exponentialSmoothing[exponentialSmoothing.length - 1];
const
adjustedEsForecast = esForecast * totalAdjustment;

// 填充表格
const
tableBody = document.getElementById('calculation-table').querySelector('tbody');
tableBody.innerHTML = '';

actualSales.forEach((sales, index) = > {
    const
row = document.createElement('tr');
const
day = index + 1;

// 移动平均数值（Day1 - 2
无值）
const
maValue = index >= 2 ? movingAverages[index - 2].toFixed(2): '-';
// 指数平滑数值
const
esValue = exponentialSmoothing[index].toFixed(2);

row.innerHTML = `
                < td >${day} < / td >
                                 < td >${sales} < / td >
                                                    < td >${maValue} < / td >
                                                                         < td >${esValue} < / td >
                                                                                              `;
tableBody.appendChild(row);
});

// 添加预测行
const
forecastRow = document.createElement('tr');
forecastRow.innerHTML = `
                        < td >${actualSales.length + 1} < / td >
                                                            < td > - < / td >
                                                                         < td >${maForecast.toFixed(2)} < / td >
                                                                                                            < td >${
                                                                                                                       adjustedEsForecast.toFixed(
                                                                                                                           2)} < / td >
                                                                                                                                   `;
tableBody.appendChild(forecastRow);

// 更新预测结果
document.getElementById('ma-forecast').textContent = maForecast.toFixed(2);
document.getElementById('es-forecast').textContent = adjustedEsForecast.toFixed(2);

// 绘制图表
const
ctx = document.getElementById('forecast-chart').getContext('2d');
new
Chart(ctx, {
    type: 'line',
    data: {
        labels: [...Array(actualSales.length).keys()].map(i= > `Day ${i + 1}
`),
datasets: [
    {
        label: 'Actual Sales',
        data: actualSales,
        borderColor: '#3b82f6',
        backgroundColor: 'rgba(59, 130, 246, 0.1)',
        borderWidth: 2,
        fill: false
    },
    {
        label: '3-Day Moving Average',
        data: [null, null, ...movingAverages],
        borderColor: '#10b981',
        backgroundColor: 'rgba(16, 185, 129, 0.1)',
        borderWidth: 2,
        borderDash: [5, 5],
        fill: false
    },
    {
        label: 'Exponential Smoothing',
        data: exponentialSmoothing,
        borderColor: '#f59e0b',
        backgroundColor: 'rgba(245, 158, 11, 0.1)',
        borderWidth: 2,
        fill: false
    }
]
},
options: {
    responsive: true,
    scales: {
        y: {beginAtZero: false, title: {display: true, text: 'Sales (units)'}},
        x: {title: {display: true, text: 'Day'}}
    },
    plugins: {
        legend: {position: 'bottom'}
    }
}
});

// 显示结果区域
document.getElementById('result-container').style.display = 'block';
});
< / script >
    < / body >
        < / html >